﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using log4net;

namespace AirlineServer
{
    public enum FlightType
    {
        Arriving,
        Departing,
    }

    [ServiceContract]
    public interface IClusterService
    {
        [OperationContract]
        void Replicate(string name, IEnumerable<Flight> flights);

        [OperationContract]
        void DiscardReplication(string name);

        [OperationContract]
        List<Flight> Search(string server, string location, FlightType type, DateTime from, DateTime to);
    }
    
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    internal class ClusterService : IClusterService
    {
        private readonly ILog log = LogManager.GetLogger(typeof(ClusterService));

        private readonly ClusterManager cm;

        private readonly FlightsDatabase fdb;

        public ClusterService(ClusterManager cm, FlightsDatabase fdb)
        {
            this.cm = cm;
            this.fdb = fdb;
        }

        public void Replicate(string name, IEnumerable<Flight> flights)
        {
            try
            {
                if (cm == null)
                {
                    throw new FaultException("Cluster manager was not set");
                }

                if (log.IsDebugEnabled)
                {
                    log.Debug("Replicating server " + name);
                }

                fdb.AddServer(new FlightsServer(name, flights));

                cm.RegisterServerReplication(name);
            }
            catch (FaultException)
            {
                throw;
            }
            catch (Exception e)
            {
                if (log.IsErrorEnabled)
                {
                    log.Error("Failed to replicate server " + name, e);
                }

                throw new FaultException("Failed to replicate");
            }
        }

        public void DiscardReplication(string name)
        {
            try
            {
                if (cm == null)
                {
                    throw new FaultException("Cluster manager was not set");
                }

                if (name == cm.ServerName)
                {
                    throw new FaultException("Can't discard replication for self");
                }

                if (log.IsDebugEnabled)
                {
                    log.Debug("Discarding replications for server " + name);
                }

                fdb.RemoveServer(name);

                cm.UnregisterReplicatedServerBySelf(name);
            }
            catch (FaultException)
            {
                throw;
            }
            catch (Exception e)
            {
                if (log.IsErrorEnabled)
                {
                    log.Error("Failed to discard replication for server " + name, e);
                }

                throw new FaultException("Failed to discard replication");
            }
        }

        public List<Flight> Search(string server, string location, FlightType type, DateTime from, DateTime to)
        {
            try
            {
                var flights = fdb.GetServerFlights(server);

                var res = from flight in flights
                          where (type == FlightType.Departing ? flight.From == location : flight.To == location) &&
                          flight.Date >= @from &&
                          flight.Date <= to
                          select flight;

                return res.ToList();

            }
            catch (KeyNotFoundException)
            {
                if (log.IsWarnEnabled)
                {
                    log.Warn("Received query for non existing server " + server);
                }

                throw new FaultException("Received query for non existing server");
            }
        }
    }
}
